package cn.jsu.oj.dispatchServer.satellite.pojo.net;

import cn.jsu.oj.dispatchServer.satellite.annotation.NoDeserialize;
import cn.jsu.oj.dispatchServer.satellite.annotation.NoSerialize;
import cn.jsu.oj.dispatchServer.satellite.annotation.PackAuthorize;
import cn.jsu.oj.dispatchServer.satellite.annotation.PackType;
import cn.jsu.oj.dispatchServer.satellite.config.ResourceConfig;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.HashMap;
import java.util.Map;

/**
 * @author suixuexue, ahtonc
 */
public abstract class AbsPackNet {
    
    /**
     * 字节序，指定传递的字节数组为大端序或者小端序
     */
    private static final ByteOrder PACK_ORDER;

    /**
     * String序列化的编码格式
     */
    private static final String PACK_CHARSET;

    /**
     * 包开始标志
     */
    private static final String PACK_HEAD;

    /**
     * 当前包的允许的最大长度
     */
    private static final int PACK_MAX_LENGTH;

    static {
        PACK_ORDER = ResourceConfig.getConfigBoolean("client-pack-order", true) ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
        PACK_CHARSET = ResourceConfig.getConfigString("client-pack-charset");
        PACK_HEAD = ResourceConfig.getConfigString("client-pack-head");

//        PACK_MAX_LENGTH = ResourceConfig.getConfigInteger("client-pack-max-length", 31457280);
//        PACK_MAX_LENGTH = ResourceConfig.getConfigInteger("client-pack-max-length", Integer.MAX_VALUE);
        PACK_MAX_LENGTH = 31457280;
    }

    /**
     * 序列化对象成byte数组
     */
    public byte[] serialize() throws Exception {
        ByteBuffer byteBuffer = ByteBuffer.allocate(PACK_MAX_LENGTH);
        byteBuffer.order(PACK_ORDER);
        byteBuffer.putInt(PACK_HEAD.length());
        byteBuffer.put(PACK_HEAD.getBytes(PACK_CHARSET));
        int oldPosition = byteBuffer.position();
        byteBuffer.position(oldPosition + 4);
        byteBuffer.putShort(this.getTypeNo());
        ObjectSerialize.serialize(byteBuffer, this);
        byteBuffer.putInt(oldPosition, byteBuffer.position() - oldPosition - 4);
        byteBuffer.flip();
        byte[] array = new byte[byteBuffer.limit() + 1];
        System.arraycopy(byteBuffer.array(), 0, array, 0, byteBuffer.limit() + 1);
        return array;
    }

    /**
     * 通过ByteBuffer和class获取反序列化的对象
     *
     * @param byteBuffer 缓冲区
     * @param clazz      反序列化类的class
     */
    private static AbsPackNet getPack(ByteBuffer byteBuffer, Class<?> clazz) throws Exception {
        AbsPackNet pack = (AbsPackNet) clazz.getDeclaredConstructor().newInstance();
        ObjectSerialize.deserialize(byteBuffer, pack);
        return pack;
    }

    /**
     * 通过byte数组获取反序列化的对象
     *
     * @param data 数据
     */
    public static AbsPackNet deserialize(byte[] data) throws Exception {
        ByteBuffer byteBuffer = ByteBuffer.wrap(data);
        byteBuffer.order(PACK_ORDER);
        return deserialize(byteBuffer);
    }

    /**
     * 通过ByteBuffer获取反序列化的对象
     *
     * @param byteBuffer 缓冲区
     */
    private static AbsPackNet deserialize(ByteBuffer byteBuffer) throws Exception {
        short typeNo = byteBuffer.getShort();
        Class<?> clazz = PackAuthorize.getInstance().getClass(typeNo);
        return getPack(byteBuffer, clazz);
    }

    /**
     * 获取包编号
     */
    public final short getTypeNo() {
        PackType packType = (this.getClass().getAnnotation(PackType.class));
        return packType.typeNo();
    }

    private static class ObjectSerialize {
        /**
         * 序列化实体类
         *
         * @param buffer 缓冲区
         * @param o      实体类
         */
        public static void serialize(ByteBuffer buffer, Object o) throws Exception {
            if (o == null) {
                return;
            }
            Class<?> clazz = o.getClass();
            PackType annotation = o.getClass().getAnnotation(PackType.class);
            for (Class<?> clz : annotation.superClass()) {
                if (!clz.isAssignableFrom(clazz)) {
                    continue;
                }
                for (Field field : clz.getDeclaredFields()) {
                    if (field.getAnnotation(NoSerialize.class) == null) {
                        field.setAccessible(true);
                        serializeField(buffer, field.get(o), field.getType());
                    }
                }
            }
            for (Field field : o.getClass().getDeclaredFields()) {
                if (field.getAnnotation(NoSerialize.class) == null) {
                    field.setAccessible(true);
                    if (field.get(o) == null) {
                        continue;
                    }
                    serializeField(buffer, field.get(o), field.getType());
                }
            }
        }

        /**
         * 序列化实体类的属性
         *
         * @param buffer 缓冲区
         * @param o      属性的值
         * @param clazz  属性的class
         */
        private static void serializeField(ByteBuffer buffer, Object o, Class<?> clazz) throws Exception {
            if (clazz == String.class) {
                serializeString(buffer, (String) o);
                return;
            }
            if (clazz.isArray()) {
                serializeArray(buffer, o);
                return;
            }
            serializeBaseField(buffer, o, clazz);
        }

        /**
         * 序列化String
         *
         * @param buffer 缓冲区
         * @param s      字符串
         */
        private static void serializeString(ByteBuffer buffer, String s) throws Exception {
            if (s == null) {
                buffer.putInt(0);
            } else {
                byte[] bs = s.getBytes(PACK_CHARSET);
                buffer.putInt(bs.length);
                System.out.println("position: " + buffer.position() + "limit: " +
                        buffer.limit() + "capicty: " + buffer.capacity());
                buffer.put(bs);
            }
        }

        /**
         * 序列化数组
         *
         * @param buffer 缓冲区
         * @param o      实体类
         */
        private static void serializeArray(ByteBuffer buffer, Object o) throws Exception {
            if (o == null) {
                buffer.putInt(0);
                return;
            }
            int length = Array.getLength(o);
            buffer.putInt(length);
            Class<?> type = o.getClass().getComponentType();
            for (int i = 0; i < length; i++) {
                if (type == String.class) {
                    serializeString(buffer, (String) Array.get(o, i));
                } else {
                    serializeBaseField(buffer, Array.get(o, i), type);
                }
            }
        }


        /**
         * 基本类型class映射,用于加快匹配
         */
        private static final Map<Class<?>, Integer> BASE_FIELD_MAP;

        static {
            BASE_FIELD_MAP = new HashMap<>();
            BASE_FIELD_MAP.put(boolean.class, 1);
            BASE_FIELD_MAP.put(Boolean.class, 1);

            BASE_FIELD_MAP.put(byte.class, 2);
            BASE_FIELD_MAP.put(Byte.class, 2);

            BASE_FIELD_MAP.put(short.class, 3);
            BASE_FIELD_MAP.put(Short.class, 3);

            BASE_FIELD_MAP.put(int.class, 4);
            BASE_FIELD_MAP.put(Integer.class, 4);

            BASE_FIELD_MAP.put(float.class, 5);
            BASE_FIELD_MAP.put(Float.class, 5);

            BASE_FIELD_MAP.put(long.class, 6);
            BASE_FIELD_MAP.put(Long.class, 6);

            BASE_FIELD_MAP.put(double.class, 7);
            BASE_FIELD_MAP.put(Double.class, 7);
        }

        /**
         * 序列化JAVA基本类型
         *
         * @param buffer 缓冲区
         * @param o      实体类
         * @param clazz  属性的class
         */
        private static void serializeBaseField(ByteBuffer buffer, Object o, Class<?> clazz) throws Exception {
            switch (BASE_FIELD_MAP.get(clazz)) {
                case 1:
                    //boolean使用8位
                    buffer.put(((boolean) o) ? (byte) 1 : (byte) 0);
                    break;
                case 2:
                    buffer.put(((Number) o).byteValue());
                    break;
                case 3:
                    buffer.putShort(((Number) o).shortValue());
                    break;
                case 4:
                    buffer.putInt(((Number) o).intValue());
                    break;
                case 5:
                    buffer.putFloat(((Number) o).floatValue());
                    break;
                case 6:
                    buffer.putLong(((Number) o).longValue());
                    break;
                case 7:
                    buffer.putDouble(((Number) o).doubleValue());
                    break;
                default:
                    throw new Exception("没有" + clazz.getSimpleName() + "基本类型");
            }
        }

        /**
         * 解包
         *
         * @param buffer 缓冲区
         * @param o      实体类
         */
        public static void deserialize(ByteBuffer buffer, Object o) throws Exception {
            if (o == null) {
                return;
            }
            Class<?> clazz = o.getClass();
            PackType annotation = o.getClass().getAnnotation(PackType.class);
            for (Class<?> clz : annotation.superClass()) {
                if (!clz.isAssignableFrom(clazz)) {
                    continue;
                }
                for (Field field : clz.getDeclaredFields()) {
                    if (field.getAnnotation(NoDeserialize.class) == null) {
                        field.setAccessible(true);
                        field.set(o, deserializeField(buffer, field.getType()));
                    }
                }
            }
            for (Field field : clazz.getDeclaredFields()) {
                if (buffer.position() == buffer.limit()) {
                    break;
                }
                if (field.getAnnotation(NoDeserialize.class) == null) {
                    field.setAccessible(true);
                    field.set(o, deserializeField(buffer, field.getType()));
                }
            }
        }

        /**
         * 反序列化属性
         *
         * @param buffer 缓冲区
         * @param clazz  属性class
         * @return 属性值
         */
        private static Object deserializeField(ByteBuffer buffer, Class<?> clazz) throws Exception {
            if (clazz == String.class) {
                return deserializeString(buffer);
            }
            if (clazz.isArray()) {
                return deserializeArray(buffer, clazz);
            }
            return deserializeBaseField(buffer, clazz);
        }

        /**
         * 反序列化字符串
         *
         * @param buffer 缓冲区
         * @return 字符串
         */
        private static String deserializeString(ByteBuffer buffer) throws Exception {
            String str = null;
            //获取String长度
            int length = buffer.getInt();
            if (length >= 0) {
                byte[] bytes = new byte[length];
                buffer.get(bytes);
                //转换编码
                str = new String(bytes, PACK_CHARSET);
            }
            return str;
        }

        /**
         * 反序列化数组
         *
         * @param buffer 缓冲区
         * @param clazz  数组class
         * @return 数组值
         */
        private static Object deserializeArray(ByteBuffer buffer, Class<?> clazz) throws Exception {
            int length = buffer.getInt();
            if (length < 0) {
                throw new Exception("deserializeArray length < 0");
            }
            Class<?> componentType = clazz.getComponentType();
            boolean isString = componentType == String.class;
            Object array = Array.newInstance(clazz.getComponentType(), length);
            for (int i = 0; i < length; i++) {
                if (isString) {
                    Array.set(array, i, deserializeString(buffer));
                } else {
                    Array.set(array, i, deserializeBaseField(buffer, componentType));
                }
            }
            return array;
        }

        /**
         * 反序列化基本类型
         *
         * @param buffer 缓冲区
         * @param clazz  属性class
         * @return 属性值
         */
        private static Object deserializeBaseField(ByteBuffer buffer, Class<?> clazz) throws Exception {
            switch (BASE_FIELD_MAP.get(clazz)) {
                case 1:
                    //boolean使用8位
                    return buffer.get() == 0 ? Boolean.FALSE : Boolean.TRUE;
                case 2:
                    return buffer.get();
                case 3:
                    return buffer.getShort();
                case 4:
                    return buffer.getInt();
                case 5:
                    return buffer.getFloat();
                case 6:
                    return buffer.getLong();
                case 7:
                    return buffer.getDouble();
                default:
                    throw new Exception("没有" + clazz.getSimpleName() + "基本类型");
            }
        }
    }
}